package com.effectiv.gooruda.rest.ext;

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;

import jodd.cache.LRUCache;
import jodd.util.StringUtil;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.effectiv.gooruda.domain.Application;
import com.effectiv.gooruda.repository.ApplicationRepository;

@Component
@Provider
@Slf4j
public class SecurityRequestFilter implements ContainerRequestFilter {

	private LRUCache<String, Application> cache = new LRUCache<String, Application>(
			100);
	
	
	@Autowired
	private ApplicationRepository repository;

	public void filter(ContainerRequestContext context) throws IOException {
		String signature = context.getHeaders().getFirst("signature");
		String publicKey = context.getHeaders().getFirst("public-key");

		log.debug("signature = {}", signature);
		log.debug("publicKey = {}", publicKey);
		
		
		String uri = context.getUriInfo().getAbsolutePath().toString();
		log.debug("getAbsolutePath = {}", uri);
		
		if(StringUtil.indexOfIgnoreCase(uri, "apidocs") != -1){
			return;
		}

		// get application in cache first
		Application app = cache.get(publicKey);
		if (app == null) {
			// Fallback on DB
			log.debug("LOoking in DB because of cache miss.");
			app = repository.findByPublicKey(publicKey);
			log.debug("app = {}", app);
		}
		// failed in db also.
		if (app == null) {

			ResponseBuilder responseBuilder = Response
					.status(Status.UNAUTHORIZED);

			ExtResponse extResponse = new ExtResponse();
			extResponse.setErrorCode(5000);
			extResponse
					.setMessage("No application found for the given public key. Unauthorized request.");

			responseBuilder.entity(extResponse);

			context.abortWith(responseBuilder.build());

		} else {
			cache.put(publicKey, app);

			// form the signature with salt
			String hash = getHash(app);

			// match the signature
			if (!StringUtil.equals(signature, hash)) {
				ResponseBuilder responseBuilder = Response
						.status(Status.UNAUTHORIZED);
				
				ExtResponse extResponse = new ExtResponse();
				extResponse.setErrorCode(5001);
				extResponse
						.setMessage("Signature mismatch. Unauthorized request.");

				responseBuilder.entity(extResponse);
				
				

				context.abortWith(responseBuilder.build());
			}
		}
		
		//put app in threadlocal
		SecurityContextHolder.set(app);
	}

	private String getHash(Application app) {
		String secret = app.getPrivateKey();
		String salt = app.getSalt();

		return PasswordEncoder.encode(secret, salt);
	}

}
